﻿using System.Collections;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Principal;
using System.Web.Mvc.Html;
using System.Web.Routing;
using Microsoft.Web.Mvc;
using TSharp.Core.Mvc.Extensions;

namespace System.Web.Mvc
{
    /// <summary>
    ///     生成权限actionlink
    ///     <para>2010/8/14</para>
    ///     <para>THINKPADT61</para>
    ///     <para>tangjingbo</para>
    /// </summary>
    public static class HtmlHelperRenderActionExtensions
    {
        /// <summary>
        ///     Renders the action.
        /// </summary>
        /// <typeparam name="TController">The type of the controller.</typeparam>
        /// <param name="helper">The helper.</param>
        /// <param name="action">The action.</param>
        /// <param name="values">The values.</param>
        public static void RenderActionT<TController>(this HtmlHelper helper, Expression<Action<TController>> action,
                                                      RouteValueDictionary values) where TController : IController
        {
            RouteValueDictionary rvd = ExpressionValuesHelper.GetRouteValuesFromExpression(action, values);
            helper.RenderRoute(rvd);
        }

        /// <summary>
        ///     Securities the trimmed action link.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="linkText">The link text.</param>
        /// <param name="action">The action.</param>
        /// <param name="controller">The controller.</param>
        /// <returns></returns>
        public static MvcHtmlString SecurityTrimmedActionLink(
            this HtmlHelper htmlHelper,
            string linkText,
            string action,
            string controller)
        {
            return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, false);
        }

        /// <summary>
        ///     Securities the trimmed action link.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="linkText">The link text.</param>
        /// <param name="action">The action.</param>
        /// <param name="controller">The controller.</param>
        /// <param name="showDisabled">
        ///     if set to <c>true</c> [show disabled].
        /// </param>
        /// <returns></returns>
        public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action,
                                                              string controller, bool showDisabled)
        {
            if (IsAccessibleToUser(action, controller))
            {
                return htmlHelper.ActionLink(linkText, action, controller);
            }
            else
            {
                return MvcHtmlString.Create(showDisabled ? String.Format("<span>{0}</span>", linkText) : "");
            }
        }

        /// <summary>
        ///     Determines whether [is accessible to user] [the specified action authorize].
        /// </summary>
        /// <param name="actionAuthorize">The action authorize.</param>
        /// <param name="controllerAuthorize">The controller authorize.</param>
        /// <returns>
        ///     <c>true</c> if [is accessible to user] [the specified action authorize]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsAccessibleToUser(string actionAuthorize, string controllerAuthorize)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            GetControllerType(controllerAuthorize);
            Type controllerType = GetControllerType(controllerAuthorize);
            var controller = (IController) Activator.CreateInstance(controllerType);
            var controllerAttributes =
                new ArrayList(controller.GetType().GetCustomAttributes(typeof (AuthorizeAttribute), true));
            var actionAttributes = new ArrayList();
            MethodInfo[] methods = controller.GetType().GetMethods();
            foreach (MethodInfo method in methods)
            {
                object[] attributes = method.GetCustomAttributes(typeof (ActionNameAttribute), true);
                if ((attributes.Length == 0 && method.Name == actionAuthorize) ||
                    (attributes.Length > 0 && ((ActionNameAttribute) attributes[0]).Name == actionAuthorize))
                {
                    actionAttributes.AddRange(method.GetCustomAttributes(typeof (AuthorizeAttribute), true));
                }
            }
            if (controllerAttributes.Count == 0 && actionAttributes.Count == 0)
                return true;

            IPrincipal principal = HttpContext.Current.User;
            string roles = "";
            string users = "";
            if (controllerAttributes.Count > 0)
            {
                var attribute = controllerAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }
            if (actionAttributes.Count > 0)
            {
                var attribute = actionAttributes[0] as AuthorizeAttribute;
                roles += attribute.Roles;
                users += attribute.Users;
            }

            if (string.IsNullOrEmpty(roles) && string.IsNullOrEmpty(users) && principal.Identity.IsAuthenticated)
                return true;

            string[] roleArray = roles.Split(',');
            string[] usersArray = users.Split(',');
            foreach (string role in roleArray)
            {
                if (role == "*" || principal.IsInRole(role))
                    return true;
            }
            foreach (string user in usersArray)
            {
                if (user == "*" && (principal.Identity.Name == user))
                    return true;
            }
            return false;
        }

        /// <summary>
        ///     Gets the type of the controller.
        /// </summary>
        /// <param name="controllerName">Name of the controller.</param>
        /// <returns></returns>
        public static Type GetControllerType(string controllerName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes())
            {
                if (type.BaseType.Name == "Controller" &&
                    (type.Name.ToUpper() == (controllerName.ToUpper() + "Controller".ToUpper())))
                {
                    return type;
                }
            }
            return null;
        }
    }
}